﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LibraryApi.Common;

namespace LibraryApi
{
    [Serializable]
    public class Library
    {
        private readonly List<Book> m_Books = new List<Book>();

        #region PUBLIC PROPERTIES

        public int Authors
        {
            get
            {
                var result = from book in m_Books
                             orderby book
                             group book by book.Author;
                return result.Count();
            }
        }

        public int Titles
        {
            get
            {
                var result = from book in m_Books
                             orderby book
                             group book by book.Title;
                return result.Count();
            }
        }

        public int Books
        {
            get
            {
                return m_Books.Count;
            }
        }

        public int TakenBooks
        {
            get
            {
                var result = from book in m_Books
                             orderby book
                             group book by book.TakenAt != null;
                return result.Count();
            }
        }

        #endregion

        public Library()
        {
        }

        public void Add(Book book)
        {
            var foundBook = m_Books.FirstOrDefault(b=>b.ISBN == book.ISBN);
            if (foundBook != null)
                throw new LibraryException("Book ISBN {0} already added".format(book.ISBN));

            m_Books.Add(book);
        }

        public void AddRange(params Book[] books)
        {
            foreach (Book book in books)
                Add(book);
        }

        public void Remove(Book book)
        {
            var foundBook = m_Books.FirstOrDefault(b => b.ISBN == book.ISBN);
            if (foundBook == null)
                throw new LibraryException("Book ISBN {0} not added".format(book.ISBN));

            m_Books.Remove(foundBook);
        }

        public void TakeBook(string ISBN)
        {
            var bookByISBN = FindByISBN(ISBN);
            if (bookByISBN == null)
                throw new LibraryException("Can't find book by ISBN ".concat(ISBN));

            if (bookByISBN.TakenAt != null)
                throw new LibraryException("Book by ISBN is already taken".format(ISBN));

            bookByISBN.TakenAt = DateTime.Now;
        }

        public void ReturnBook(string ISBN)
        {
            var bookByISBN = FindByISBN(ISBN);
            if (bookByISBN == null)
                throw new LibraryException("Can't find book by ISBN ".concat(ISBN));

            if (bookByISBN.TakenAt == null)
                throw new LibraryException("Book by ISBN is not taken".format(ISBN));
  
            bookByISBN.TakenAt = null;
        }

        public void Clear()
        {
            m_Books.Clear();
        }

        public IEnumerable<Book> FindByAutor(string autor)
        {
            var foundBooks = m_Books.Where(b => b.Author.Name == autor);
            foreach (var book in foundBooks)
                yield return book;
        }

        public IEnumerable<Book> FindByTitle(string title)
        {
            var foundBooks = m_Books.Where(b => b.Title == title);
            foreach (var book in foundBooks)
                yield return book;
        }

        public Book FindByISBN(string ISBN)
        {
            var foundBook = m_Books.FirstOrDefault(b => b.ISBN == ISBN);
            return foundBook;
        }

        public static Library Load(string fileName)
        {
            if (!File.Exists(fileName))
                throw new FileNotFoundException();

            var buffer = File.ReadAllBytes(fileName);
            return ObjectSerializer.Load<Library>(buffer);
        }

        public static void Save(string fileName, Library library)
        {
            if (!Directory.Exists(Path.GetDirectoryName(fileName)))
                throw new DirectoryNotFoundException();

            var buffer = ObjectSerializer.Save<Library>(library);
            File.WriteAllBytes(fileName, buffer);
        }
    }

    public class LibraryException : Exception
    {
        public LibraryException()
        {
        }

        public LibraryException(string message)
            : base(message)
        {
        }
    }
}
